/*******************************************************************************
 *	                           bsp nor spi.c
 *******************************************************************************/
#include "bsp.h"
#include "bsp_swspi_nor.h"
#include "pincfg.h"
#include "nor_cmd.h"
#include "mytype.h"

/*****************************************************************************
 * @brief   IO 描述
 *****************************************************************************/
#define qo_0 PEout(2)
#define qo_1 PEout(3)
#define qo_2 PEout(4)
#define qo_3 PEout(5)
#define qi_0 PEin(2)
#define qi_1 PEin(3)
#define qi_2 PEin(4)
#define qi_3 PEin(5)
#define s_cs PEout(0)
#define ssck PEout(6)
#define mosi qo_0
#define miso qi_1
#define s_wp qo_2
#define hold qo_3

#define qpi_out(x) GPIOE->scr = (((x) << 2) | (0x0F0000 << 2))
#define qpi_in() ((GPIOE->idt & 0x3C) >> 2)

#define md2(bit, md) ((uint32_t)md << bit * 2)
#define md1(bit, md) ((uint32_t)md << bit)

#define msk_2 (md2(0, 3) | md2(2, 3) | md2(3, 3) | md2(4, 3) | md2(5, 3) | md2(6, 3))
#define msk_1 (md1(0, 1) | md1(2, 1) | md1(3, 1) | md1(4, 1) | md1(5, 1) | md1(6, 1))

/*****************************************************************************
 * @brief   hal init.
 * @param   none
 * @return  none
 *****************************************************************************/
void nor_spi_init(void)
{
	intx_alloc();

	intx_disable();

	// spi io mode
	GPIOE->cfgr &= ~msk_2;
	GPIOE->cfgr |= (md2(0, 1) | md2(2, 1) | md2(3, 0) | md2(4, 1) | md2(5, 1) | md2(6, 1));
	// io type -> pp
	GPIOE->omode &= ~msk_1;
	// io divrer -> high
	GPIOE->odrvr &= ~msk_2;
	GPIOE->odrvr |= (md2(0, 1) | md2(2, 1) | md2(3, 1) | md2(4, 1) | md2(5, 1) | md2(6, 1));
	// none pull down / up
	GPIOE->pull &= ~msk_2;

	intx_enable();

	// io st
	s_cs = 1;
	s_wp = 1;
	hold = 1;
	ssck = 1;
	mosi = 1;

	// send ff
	nor_spi_wb(0xFF);
	nor_spi_wb(0xFF);
	nor_spi_wb(0xFF);
	nor_spi_wb(0xFF);
}

/*****************************************************************************
 * @brief   cs ctr.
 * @param   none
 * @return  none
 * @ Pass/ Fail criteria: none
 *****************************************************************************/
void nor_spi_cs(uint8_t st)
{
	if (st)
	{
		s_cs = 1;
		NOP4();
	}
	else
	{
		s_cs = 0;
		NOP4();
	}
}

/*****************************************************************************
 * @brief   spi mode select.
 *****************************************************************************/
void nor_hal_mode_spi(void)
{
	intx_alloc();

	intx_disable();
	GPIOE->cfgr &= ~(md2(2, 3) | md2(3, 3) | md2(4, 3) | md2(5, 3));
	GPIOE->cfgr |= +(md2(2, 1) | md2(3, 0) | md2(4, 1) | md2(5, 1));
	intx_enable();
};

void nor_hal_mode_qpi_out(void)
{
	intx_alloc();

	intx_disable();
	GPIOE->cfgr |= +(md2(2, 1) | md2(3, 1) | md2(4, 1) | md2(5, 1));
	intx_enable();
};

void nor_hal_mode_qpi_in(void)
{
	intx_alloc();

	intx_disable();
	GPIOE->cfgr &= ~(md2(2, 3) | md2(3, 3) | md2(4, 3) | md2(5, 3));
	intx_enable();
};

/*****************************************************************************
 * @brief   spi delay.
 *****************************************************************************/
#define sig_keep() NOP2()

//////////////////////////////////////////////////////////////////////////////
#define spi_read_bit() \
	ssck = 0;          \
	sig_keep();        \
	ssck = 1;          \
	sig_keep();        \
	rreg <<= 1;        \
	rreg |= miso;

#define spi_write_bit() \
	ssck = 0;           \
	mosi = (wreg >> 7); \
	wreg <<= 1;         \
	sig_keep();         \
	ssck = 1;           \
	sig_keep();

/*****************************************************************************
 * @brief   spi data read.
 *****************************************************************************/
uint8_t nor_spi_rb(void)
{
	uu8 rreg = 0;

	spi_read_bit();
	spi_read_bit();
	spi_read_bit();
	spi_read_bit();

	spi_read_bit();
	spi_read_bit();
	spi_read_bit();
	spi_read_bit();

	return rreg;
}

void nor_spi_wb(uint8_t dataW)
{
	uu8 wreg = dataW;

	spi_write_bit();
	spi_write_bit();
	spi_write_bit();
	spi_write_bit();

	spi_write_bit();
	spi_write_bit();
	spi_write_bit();
	spi_write_bit();
}

/*****************************************************************************
 * @brief   spi data write.
 *****************************************************************************/
uint8_t nor_spi_write(const uint8_t *dataw, uint32_t Len)
{
	uu8 wreg;
	while (Len--)
	{
		uu8 wreg = *dataw++;

		spi_write_bit();
		spi_write_bit();
		spi_write_bit();
		spi_write_bit();

		spi_write_bit();
		spi_write_bit();
		spi_write_bit();
		spi_write_bit();
	}

	return (0);
}

/*****************************************************************************
 * @brief   spi data read.
 *****************************************************************************/
uint8_t nor_spi_read(uint8_t *datar, uint32_t Len)
{
	uu32 rreg;

	while (Len--)
	{
		rreg = 0;

		spi_read_bit();
		spi_read_bit();
		spi_read_bit();
		spi_read_bit();

		spi_read_bit();
		spi_read_bit();
		spi_read_bit();
		spi_read_bit();

		*datar++ = rreg;
	}

	return 0;
}
//////////////////////////////////////////////////////////////////////////////
#define qpi_read_bits(n) \
	ssck = 0;            \
	sig_keep();          \
	ssck = 1;            \
	sig_keep();          \
	rreg |= qpi_in() << 4 * n;

#define qpi_write_bits(n)                         \
	ssck = 0;                                     \
	qpi_out((wreg & (0x0FUL << 4 * n)) >> n * 4); \
	sig_keep();                                   \
	ssck = 1;                                     \
	sig_keep();

/*****************************************************************************
 * @brief   qpi data write.
 *****************************************************************************/
uint8_t nor_qpi_write(const uint8_t *dataw, uint32_t Len)
{
	uu32 wreg;

	while (Len--)
	{
		wreg = *dataw++;
		qpi_write_bits(1);
		qpi_write_bits(0);
	}

	return 0;
}

/*****************************************************************************
 * @brief   qpi data read.
 *****************************************************************************/
uint8_t nor_qpi_read(uint8_t *datar, uint32_t Len)
{
	uu32 rreg;
	uint32_t len_alain = (uint32_t)datar & 0x03;

	// 8 bits mode to alain 4B
	if (len_alain)
	{
		len_alain = 4 - len_alain;
		Len -= len_alain;

		while (len_alain--)
		{
			rreg = 0;
			qpi_read_bits(1);
			qpi_read_bits(0);
			*datar++ = rreg;
		}
	}

	// 32 bits mode
	if (Len >= 4)
	{
		uint32_t *prd32, len32;
		len32 = Len >> 2;
		prd32 = (uint32_t *)datar;

		while (len32--)
		{
			rreg = 0;
			qpi_read_bits(1);
			qpi_read_bits(0);
			qpi_read_bits(3);
			qpi_read_bits(2);
			qpi_read_bits(5);
			qpi_read_bits(4);
			qpi_read_bits(7);
			qpi_read_bits(6);
			*prd32++ = rreg;
		}

		datar = (uint8_t *)prd32;
		Len &= 3;
	}

	// 8 bits mode
	while (Len--)
	{
		rreg = 0;
		qpi_read_bits(1);
		qpi_read_bits(0);
		*datar++ = rreg;
	}

	return 0;
}

/*****************************************************************************
 * @brief   nor spi
 *****************************************************************************/
const spi_flash_hal_type sw_spi_nor = {
	//* extern api
	.cs = nor_spi_cs,
	.mode_spi = nor_hal_mode_spi,
	.mode_qpi_out = nor_hal_mode_qpi_out,
	.mode_qpi_in = nor_hal_mode_qpi_in,
	.spi_rb = nor_spi_rb,
	.spi_wb = nor_spi_wb,
	.spi_write = nor_spi_write,
	.spi_read = nor_spi_read,
	.qpi_write = nor_qpi_write,
	.qpi_read = nor_qpi_read,
};
